﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;

namespace DBUtil;

/// <summary>
/// 从 Class 实体定义中分析到的表信息
/// </summary>
public class EntityInfo : ICloneable
{
    /// <summary>
    /// 封装了表的 pureName, metaDataSegName, quotedName
    /// </summary>
    public TableName TableName { get; set; }
    /// <summary>
    /// 如: typeof(PersonEntity)
    /// </summary>
    public Type Type { get; set; }
    /// <summary>
    /// 通过 typeof(PersonEntity).GetClassFullName() 得到的名字
    /// </summary>
    public string TypeClassFullName { get; set; }
    /// <summary>
    /// 属性列集合
    /// </summary>
    public List<EntityPropertyInfo> Props { get; set; } = [];

    #region Clone
    /// <summary>
    /// 克隆数据
    /// </summary>
    public EntityInfo CloneEntityInfo()
    {
        var entityInfo = new EntityInfo()
        {
            TableName = TableName.Clone() as TableName,
            Type = Type,
            TypeClassFullName = TypeClassFullName,
            Props = Props.Select(i => new EntityPropertyInfo()
            {
                /*各种Name*/
                PropNamePure = i.PropNamePure,
                PropNameQuoted = i.PropNameQuoted,
                ColumnNamePure = i.ColumnNamePure,
                ColumnNameQuoted = i.ColumnNameQuoted,

                /*Json配置判断*/
                IsJsonMap = i.IsJsonMap,

                /*SelectFunc & SelectValueFunc*/
                SelectFunc = i.SelectFunc,
                SelectValueFunc = i.SelectValueFunc,

                /*Ignore*/
                IsIgnoreInsert = i.IsIgnoreInsert,
                IsIgnoreSelect = i.IsIgnoreSelect,
                IsIgnoreUpdate = i.IsIgnoreUpdate,

                /*主键*/
                IsPrimaryKey = i.IsPrimaryKey,
                PrimaryKeyStrategy = i.PrimaryKeyStrategy,

                /*枚举*/
                IsEnum = i.IsEnum,
                IsFlagEnum = i.IsFlagEnum,

                /*日期时间*/
                ActiveTimeFormat = i.ActiveTimeFormat,
                IsDateTime = i.IsDateTime,
                IsDate = i.IsDate,
                IsTime = i.IsTime,
                IsTimeSpan = i.IsTimeSpan,
                TimeFormat = i.TimeFormat,

                /*数据类型*/
                PropertyInfo = i.PropertyInfo,
                Type = i.Type,
                DefaultValue = i.DefaultValue,
                TypeClassFullName = i.TypeClassFullName,
                IsNullAbleStruct = i.IsNullAbleStruct,
                TypeCode = i.TypeCode,

                /*tree*/
                IsParentId = i.IsParentId,
                IsChildren = i.IsChildren,

                /*其他*/
                IsColumnNameEqualPropName = i.IsColumnNameEqualPropName,
                TypeName = i.TypeName,
                Order = i.Order,
                IsDbString = i.IsDbString,
                CanMapToTable = i.CanMapToTable,
            }).ToList()
        };
        return entityInfo;
    }

    public object Clone() => CloneEntityInfo();
    #endregion
    /// <summary>
    /// 单个主键,当联合主键时,使用:
    /// <code>
    /// entityInfo.EntityPropertyInfos.Where(i => i.IsPrimaryKey).ToList()
    /// </code>
    /// </summary>
    public EntityPropertyInfo PrimaryKeyColumn => Props.FirstOrDefault(i => i.IsPrimaryKey);
}

/// <summary>
/// 从 class 实体的属性定义中分析到的列信息
/// </summary>
public class EntityPropertyInfo
{
    #region 各种Name PropNamePure/PropNameQuoted ColumnNamePure/ColumnNameQuoted
    /// <summary>
    /// 属性名称(纯净), 如: public string Name { get; set; } //col.PropNamePure.ShouldBe("Name")
    /// </summary>
    /// <remarks>
    /// 注意: 因为属性名字可能是 sql 中的关键字, 所以这个 <c>PropNamePure</c> 不能用在sql拼接中. sql拼接时请使用 <seealso cref="PropNameQuoted"/>
    /// </remarks>
    public string PropNamePure { get; set; }
    /// <summary>
    /// 属性名称(带 quote 符), 如(sqlserver): public string Name { get; set; } //col.PropNameQuoted.ShouldBe("[Name]")
    /// </summary>
    /// <remarks>注意: 这个名字可以直接跟到 select 中做为别名, 如: select remark {col.PropNameQuoted} from test</remarks>
    public string PropNameQuoted { get; set; }

    /// <summary>
    /// 映射到db中的列名称(纯净), 如下:
    /// <code>
    /// public class PersonEntity
    /// {
    ///     public int Id { get; set; } //col.ColumnNamePure.ShouldBe("Id")
    ///     
    ///     [Column("name")]
    ///     public string Name { get; set; } //col.ColumnNamePure.ShouldBe("name")
    ///     
    ///     [JsonMap]
    ///     public JsonObject Properties { get; set; } //col.ColumnNamePure.ShouldBe("Properties")
    ///     
    ///     [Column("labels2")]
    ///     [JsonMap]
    ///     public List&lt;string> OtherLabels { get; set; } //col.ColumnNamePure.ShouldBe("labels2")
    ///     
    ///     [JsonMap]
    ///     public ExtInfo Ext { get; set; } //col.ColumnNamePure.ShouldBe("Ext")
    ///     
    ///     public ExtInfo Ext2 { get; set; } //col.ColumnNamePure.ShouldBe("Ext2"), 注意: 这个不应该映射到db, 它的 <see cref="CanMapToTable"/> 值为 false, 因为它不是简单类型, 也没有配置 [JsonMap]
    /// }
    /// public class ExtInfo {}
    /// </code>
    /// </summary>
    public string ColumnNamePure { get; set; }

    /// <summary>
    /// 映射到db中的列名称(带 quoted 符), 如下(sqlserver):
    /// <code>
    /// public class PersonEntity
    /// {
    ///     public int Id { get; set; } //col.ColumnNameQuoted.ShouldBe("[Id]")
    ///     
    ///     [Column("name")]
    ///     public string Name { get; set; } //col.ColumnNameQuoted.ShouldBe("[name]")
    ///     
    ///     [JsonMap]
    ///     public List&lt;string> Labels { get; set; } //col.ColumnNameQuoted.ShouldBe("[Labels]")
    ///     
    ///     [Column("labels2")]
    ///     [JsonMap]
    ///     public List&lt;string> OtherLabels { get; set; } //col.ColumnNameQuoted.ShouldBe("[labels2]")
    ///     
    ///     [JsonMap]
    ///     public ExtInfo Ext { get; set; } //col.ColumnNameQuoted.ShouldBe("[Ext]")
    ///     
    ///     public ExtInfo Ext2 { get; set; } //col.ColumnNameQuoted.ShouldBe("[Ext2]"), 注意: 这个不应该映射到db, 它的 <see cref="CanMapToTable"/> 值为 false, 因为它不是简单类型, 也没有配置 [JsonMap]
    /// }
    /// public class ExtInfo {}
    /// </code>
    /// </summary>
    public string ColumnNameQuoted { get; set; }
    #endregion

    #region Json配置判断
    /// <summary>
    /// 是否是 [JsonMap] 标记的属性
    /// </summary>
    public bool IsJsonMap { get; set; }
    #endregion

    #region SelectFunc & SelectValueFunc
    /// <summary>
    /// 用来生成可能带列别名的 select 子句, 示例(mysql):
    /// <code>
    /// public class PersonEntity
    /// {
    ///     public int Id { get; set; } //列名和属性名相同, 不用带别名
    ///     
    ///     [Column("name")]
    ///     public string Name { get; set; } // 需要带别名
    ///     
    ///     [JsonMap]
    ///     [Column("labels")]
    ///     public List&lt;string> Labels { get; set; } // 需要带别名
    /// }
    /// 
    /// entityInfo.EntityPropertyInfos.Where(i => !i.IsIgnoreSelect)
    ///     .Select(i => i.SelectFunc("t"))
    ///     .ToStringSeparated(",")
    ///     .ShouldBe("t.`Id`,t.`name` `Name`,t.`labels` `Labels`");
    /// </code>
    /// </summary>
    /// <remarks>
    /// 对比 <seealso cref="SelectValueFunc"/>
    /// </remarks>
    public Func<string, string> SelectFunc { get; set; }
    /// <summary>
    /// 用来生成不带列别名的 select 子句, 示例(mysql):
    /// <code>
    /// public class PersonEntity
    /// {
    ///     public int Id { get; set; }
    ///     
    ///     [Column("name")]
    ///     public string Name { get; set; }
    ///     
    ///     [JsonMap]
    ///     [Column("labels")]
    ///     public List&lt;string> Labels { get; set; }
    /// }
    /// 
    /// entityInfo.EntityPropertyInfos.Where(i => !i.IsIgnoreSelect)
    ///     .Select(i => i.SelectValueFunc("t"))
    ///     .ToStringSeparated(",")
    ///     .ShouldBe("t.`Id`,t.`name`,t.`labels`");
    /// </code>
    /// </summary>
    /// <remarks>
    /// 对比 <seealso cref="SelectFunc"/>
    /// </remarks>
    public Func<string, string> SelectValueFunc { get; set; }
    #endregion

    #region Ignore
    public bool IsIgnoreUpdate { get; set; }
    public bool IsIgnoreSelect { get; set; }
    public bool IsIgnoreInsert { get; set; }
    #endregion

    #region 主键
    public bool IsPrimaryKey { get; set; }
    public EnumKeyStrategy? PrimaryKeyStrategy { get; set; }
    #endregion

    #region 枚举
    /// <summary>
    /// 是否是枚举
    /// </summary>
    /// <remarks>
    /// 注意: 已经去除 nullable 的影响, 如:
    /// <code>
    /// public class Person
    /// {
    ///     public EnumSex? Sex { get; set } // col.IsEnum 为 true
    /// }
    /// public enum EnumSex { A, B }
    /// </code>
    /// </remarks>
    public bool IsEnum { get; set; }
    /// <summary>
    /// 在 <seealso cref="IsEnum"/> 为 true 时, 进一步表示是否是位枚举
    /// </summary>
    /// <remarks>
    /// 注意: 已经去除 nullable 的影响, 如:
    /// <code>
    /// public class Person
    /// {
    ///     public EnumLabel? Label { get; set } // col.IsFlagEnum 为 true
    /// }
    /// [Flag]
    /// public enum EnumLabel { A, B }
    /// </code>
    /// </remarks>
    public bool IsFlagEnum { get; set; }
    #endregion

    #region 日期时间
    /// <summary>
    /// 是否激活 TimeFormat, 只有当 c# 中定义的是 DateTime DateOnly TimeOnly DateTimeOffset 而 db 中存储的是字符串时才会激活
    /// </summary>
    public bool ActiveTimeFormat { get; set; }
    /// <summary>
    /// c#中定义的类型是否是 DateTime 或 DateTimeOffset, 已去除 nullable 影响
    /// </summary>
    public bool IsDateTime { get; set; }
    /// <summary>
    /// c#中定义的类型是否是 DateOnly, 已去除 nullable 影响
    /// </summary>
    public bool IsDate { get; set; }
    /// <summary>
    /// c#中定义的类型是否是 TimeOnly, 已去除 nullable 影响
    /// </summary>
    public bool IsTime { get; set; }
    /// <summary>
    /// c#中定义的类型是否是 TimeSpan, 已去除 nullable 影响
    /// </summary>
    public bool IsTimeSpan { get; set; }
    /// <summary>
    /// 存储的日期时间格式(sqlite中使用text存储日期时使用, 默认: "yyyy-MM-ddTHH:mm:ss.fffzzz", 如: "2020-01-01T01:02:03.123+08:00")
    /// </summary>
    public string TimeFormat { get; set; }
    #endregion

    #region 数据类型
    /// <summary>
    /// 通过 typeof(PersonEntity).GetProperties() 获取的
    /// </summary>
    public PropertyInfo PropertyInfo { get; set; }
    /// <summary>
    /// 通过 typeof(PersonEntity).GetProperty("Id").PropertyType 获取的
    /// </summary>
    public Type Type { get; set; }
    /// <summary>
    /// c#类型的默认值, 即: default(typeof(PersonEntity).GetProperty("Id").PropertyType)
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item><c>public int Id {get;set} = 5;</c> 这个的默认值是 0, 不是 5</item>
    /// <item><c>public int? Age {get;set;}</c> 这个的默认值是 null</item>
    /// </list>
    /// </remarks>
    public object DefaultValue { set; get; }
    /// <summary>
    /// 通过 typeof(PersonEntity).GetProperty("Id").PropertyType.GetClassFullName() 得到的名字
    /// </summary>
    public string TypeClassFullName { get; set; }
    /// <summary>
    /// 当前属性类型是否是可空的, 如: int? bool? Vector2?
    /// </summary>
    /// <remarks>注意: string,class,interface 不是 IsNullAble</remarks>
    public bool IsNullAbleStruct { get; set; }

    /// <summary>
    /// 注意: 已经去除 nullable 的影响
    /// </summary>
    public TypeCode TypeCode { get; set; }
    #endregion

    #region tree
    /// <summary>
    /// 是否有 [ParentId] 标记
    /// </summary>
    public bool IsParentId { get; set; }
    /// <summary>
    /// 是否有 [Children] 标记
    /// </summary>
    public bool IsChildren { get; set; }
    #endregion

    #region 其他
    /// <summary>
    /// 标记db里的列名称是否和属性名相等 (this.PropNameQuoted == this.ColumnNameQuoted), 如:
    /// <list type="bullet">
    /// <item>/*true*/ public int Id { get;set; }</item>
    /// <item>/*true*/ [Column("Id")]public int Id { get;set; }</item>
    /// <item>/*false*/ [Column("id")]public int Id { get;set; }</item>
    /// </list>
    /// </summary>
    /// <remarks>
    /// 注意: 这个可以用来决定生成 select 语句时,是否追加别名, 如: select `Id`, `name` `Name` from test
    /// </remarks>
    public bool IsColumnNameEqualPropName { get; set; }

    /// <summary>
    /// [Column(TypeName = "varchar(50)")] 中的配置 "varchar(50)", 没有配置则为null
    /// </summary>
    public string TypeName { get; set; }
    /// <summary>
    /// [Column(Order=xxxx)] 中的配置, 默认值:-1 (系统定义的默认值就是-1)
    /// </summary>
    public int Order { get; set; } = -1;
    /// <summary>
    /// 根据 [Column(TypeName = "")] 配置判断db中是否是存储为字符串, 如果用户没有配置 则返回null
    /// </summary>
    public bool? IsDbString { get; set; }
    /// <summary>
    /// 是否可以映射到表, 如下面的 Ext 属性不能映射到表:
    /// <code>
    /// public class PersonEntity
    /// {
    ///     public int Id { get; set; }
    ///     public ExtInfo Ext { get; set; } //不能映射到表, 因为它不是简单类型, 也没有配置 [JsonMap]
    /// }
    /// public class ExtInfo
    /// {
    ///     public int Age { get; set; }
    /// }
    /// </code>
    /// </summary>
    public bool CanMapToTable { get; set; }
    #endregion
}
